<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>人脸位置检测</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
        .container { max-width: 800px; margin: 0 auto; }
        .video-container { position: relative; }
        #video { width: 100%; max-width: 640px; }
        #overlay { position: absolute; top: 0; left: 0; width: 100%; max-width: 640px; }
        .target-area { stroke: green; stroke-width: 2; fill: none; }
        .face-box { stroke: red; stroke-width: 2; fill: none; }
        .face-box.inside { stroke: green; }
        .status { margin-top: 10px; font-size: 18px; }
        .status.inside { color: green; }
        .status.outside { color: red; }
        .debug-info { font-size: 12px; color: #666; margin-top: 5px; }
    </style>
</head>
<body>
    <div class="container">
        <h1>人脸位置检测</h1>
        <div class="video-container">
            <video id="video" autoplay muted playsinline></video>
            <canvas id="overlay"></canvas>
        </div>
        <div class="status" id="status">等待初始化...</div>
        <div class="debug-info" id="debugInfo"></div>
        <button id="startBtn" disabled>开始检测</button>
    </div>

    <!-- 使用本地JS文件 -->
    <script src="tf.min.js"></script>
    <script src="blazeface.min.js"></script>

    <script>
        const video = document.getElementById('video');
        const overlay = document.getElementById('overlay');
        const ctx = overlay.getContext('2d');
        const status = document.getElementById('status');
        const debugInfo = document.getElementById('debugInfo');
        const startBtn = document.getElementById('startBtn');

        let model;
        let isDetecting = false;
        let videoWidth, videoHeight;
        let isVideoMirrored = true; // 前置摄像头通常是镜像的

        // 目标区域定义（相对于视频尺寸的比例）
        const targetAreaRatio = {
            x: 0.2,    // 从视频宽度的20%开始
            y: 0.1,    // 从视频高度的10%开始
            width: 0.6, // 占视频宽度的60%
            height: 0.8 // 占视频高度的80%
        };

        // 计算实际目标区域
        function calculateTargetArea() {
            return {
                x: targetAreaRatio.x * videoWidth,
                y: targetAreaRatio.y * videoHeight,
                width: targetAreaRatio.width * videoWidth,
                height: targetAreaRatio.height * videoHeight
            };
        }

        // 初始化摄像头
        async function initCamera() {
            try {
                status.textContent = "正在请求摄像头权限...";

                // 检查资源是否正确加载
                if (typeof tf === 'undefined' || typeof blazeface === 'undefined') {
                    throw new Error('TensorFlow.js 或 BlazeFace 库未正确加载');
                }

                // 获取摄像头流
                const stream = await navigator.mediaDevices.getUserMedia({
                    video: {
                        width: { ideal: 640 },
                        height: { ideal: 480 },
                        facingMode: 'user' // 使用前置摄像头
                    }
                });
                video.srcObject = stream;

                // 等待视频元数据加载完成
                await new Promise(resolve => {
                    video.onloadedmetadata = () => {
                        // 保存实际视频尺寸
                        videoWidth = video.videoWidth;
                        videoHeight = video.videoHeight;

                        // 设置画布尺寸与视频相同
                        overlay.width = videoWidth;
                        overlay.height = videoHeight;

                        console.log(`视频尺寸: ${videoWidth} x ${videoHeight}`);
                        resolve();
                    };
                });

                // 加载人脸检测模型
                status.textContent = "正在加载人脸检测模型...";
                model = await blazeface.load();
                status.textContent = "模型加载完成，点击开始检测";
                startBtn.disabled = false;

            } catch (error) {
                console.error('初始化失败:', error);
                status.textContent = `初始化失败: ${error.message}`;
                status.className = "status error";
            }
        }

        // 绘制目标区域
        function drawTargetArea(targetArea) {
            ctx.beginPath();
            ctx.rect(targetArea.x, targetArea.y, targetArea.width, targetArea.height);
            ctx.lineWidth = 2;
            ctx.strokeStyle = 'green';
            ctx.stroke();

            // 绘制目标区域信息
            ctx.fillStyle = 'green';
            ctx.font = '12px Arial';
            ctx.fillText(`目标区域: ${formatNumber(targetArea.x)},${formatNumber(targetArea.y)} ${formatNumber(targetArea.width)}x${formatNumber(targetArea.height)}`,
                        targetArea.x + 10, targetArea.y + 20);
        }

        // 格式化数字为两位小数
        function formatNumber(num) {
            return typeof num === 'number' ? num.toFixed(0) : '?';
        }

        // 判断人脸是否在目标区域内（改进版）
        function isFaceInsideTarget(faceBox, targetArea) {
            // 计算人脸框的中心点
            const faceCenterX = faceBox.x + faceBox.width / 2;
            const faceCenterY = faceBox.y + faceBox.height / 2;

            // 计算人脸框与目标区域的重叠区域
            const overlapX = Math.max(0, Math.min(faceBox.x + faceBox.width, targetArea.x + targetArea.width) - Math.max(faceBox.x, targetArea.x));
            const overlapY = Math.max(0, Math.min(faceBox.y + faceBox.height, targetArea.y + targetArea.height) - Math.max(faceBox.y, targetArea.y));
            const overlapArea = overlapX * overlapY;

            // 人脸面积
            const faceArea = faceBox.width * faceBox.height;

            // 重叠比例
            const overlapRatio = faceArea > 0 ? overlapArea / faceArea : 0;

            // 记录调试信息
            const debug = {
                faceBox,
                targetArea,
                faceCenterX,
                faceCenterY,
                overlapRatio
            };

            // 修改判断条件：降低重叠比例阈值，使判断更宽松
            const isInside =
                (faceCenterX >= targetArea.x && faceCenterX <= (targetArea.x + targetArea.width) &&
                 faceCenterY >= targetArea.y && faceCenterY <= (targetArea.y + targetArea.height)) ||
                overlapRatio > 0.4 // 从0.2降低到0.15，使判断更宽松

            return { isInside, debug };
        }

        // 检测人脸并判断位置
        async function detectFaces() {
            if (!isDetecting) return;

            // 清除画布
            ctx.clearRect(0, 0, overlay.width, overlay.height);

            // 计算实际目标区域
            const targetArea = calculateTargetArea();

            // 绘制目标区域
            drawTargetArea(targetArea);

            try {
                // 检测人脸
                const predictions = await model.estimateFaces(video, {
                    returnTensors: false, // 确保返回普通JavaScript对象而不是张量
                    flipHorizontal: isVideoMirrored // 根据视频是否镜像调整模型输出
                });

                let hasFaceInside = false;
                let debugMessages = [];

                if (predictions.length > 0) {
                    // 处理检测到的人脸
                    for (const face of predictions) {
                        try {
                            // 获取人脸边界框
                            let start, end;
                            
                            // 处理可能的张量数据
                            if (face.topLeft && face.topLeft.arraySync) {
                                // 如果是张量，转换为普通数组
                                start = face.topLeft.arraySync();
                                end = face.bottomRight.arraySync();
                            } else if (Array.isArray(face.topLeft)) {
                                // 如果已经是数组
                                start = face.topLeft;
                                end = face.bottomRight;
                            } else {
                                console.warn('无法识别的人脸数据格式:', face);
                                continue;
                            }

                            // 验证数据有效性
                            if (!Array.isArray(start) || start.length < 2 ||
                                !Array.isArray(end) || end.length < 2 ||
                                typeof start[0] !== 'number' || typeof start[1] !== 'number' ||
                                typeof end[0] !== 'number' || typeof end[1] !== 'number') {
                                console.warn('无效的人脸位置数据:', face);
                                continue;
                            }

                            const faceBox = {
                                x: start[0],
                                y: start[1],
                                width: end[0] - start[0],
                                height: end[1] - start[1]
                            };

                            // 判断人脸是否在目标区域内
                            const { isInside, debug } = isFaceInsideTarget(faceBox, targetArea);
                            
                            // 增加更详细的调试信息显示
                            const detailedDebugMsg = `人脸: ${formatNumber(faceBox.x)},${formatNumber(faceBox.y)},${formatNumber(faceBox.width)}x${formatNumber(faceBox.height)}, 重叠率: ${(debug.overlapRatio * 100).toFixed(1)}%, 中心点: (${formatNumber(debug.faceCenterX)},${formatNumber(debug.faceCenterY)}), ${isInside ? '在区域内' : '不在区域内'}`;
                            debugMessages.push(detailedDebugMsg);
                            
                            // 在页面上显示更多调试信息
                            debugInfo.innerHTML = `检测到 ${predictions.length} 个人脸<br>目标区域: ${formatNumber(targetArea.x)},${formatNumber(targetArea.y)},${formatNumber(targetArea.width)}x${formatNumber(targetArea.height)}<br>${detailedDebugMsg}`;

                            // 更新状态
                            if (isInside) {
                                hasFaceInside = true;
                            }

                            // 绘制人脸框
                            ctx.beginPath();
                            ctx.rect(faceBox.x, faceBox.y, faceBox.width, faceBox.height);
                            ctx.lineWidth = 2;
                            ctx.strokeStyle = isInside ? 'green' : 'red';
                            ctx.stroke();

                            // 绘制人脸位置信息
                            ctx.fillStyle = ctx.strokeStyle;
                            ctx.font = '12px Arial';
                            ctx.fillText(`人脸: ${formatNumber(faceBox.x)},${formatNumber(faceBox.y)} ${formatNumber(faceBox.width)}x${formatNumber(faceBox.height)}`,
                                        faceBox.x + 10, faceBox.y + 20);
                        } catch (faceError) {
                            console.error('处理人脸数据时出错:', faceError);
                        }
                    }

                    // 更新总体状态
                    if (hasFaceInside) {
                        status.textContent = "人脸在规定范围内";
                        status.className = "status inside";
                    } else {
                        status.textContent = "人脸不在规定范围内，请调整位置";
                        status.className = "status outside";
                    }

                    // 更新调试信息
                    console.log(debugMessages.join('\n'));

                } else {
                    status.textContent = "未检测到人脸";
                    status.className = "status outside";
                    debugInfo.textContent = "未检测到人脸";
                }

            } catch (error) {
                console.error('人脸检测错误:', error);
                status.textContent = "人脸检测错误";
                status.className = "status error";
                debugInfo.textContent = `错误: ${error.message}`;
            }

            // 继续下一帧检测
            requestAnimationFrame(detectFaces);
        }

        // 开始检测按钮点击事件
        startBtn.addEventListener('click', () => {
            if (!isDetecting) {
                isDetecting = true;
                startBtn.textContent = "停止检测";
                detectFaces();
            } else {
                isDetecting = false;
                startBtn.textContent = "开始检测";
                ctx.clearRect(0, 0, overlay.width, overlay.height);
                const targetArea = calculateTargetArea();
                drawTargetArea(targetArea);
                status.textContent = "检测已停止";
                status.className = "status";
                debugInfo.textContent = "";
            }
        });

        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', initCamera);
    </script>
</body>
</html>